package com.ca.project.Server.Modules.User.Service.Impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ca.project.Redis.Constants.RedisConstants;
import com.ca.project.Server.Modules.User.Context.UserRegisterContext;
import com.ca.project.Server.Modules.User.Converter.UserConverter;
import com.ca.project.Server.Modules.User.Entity.UserId;
import com.ca.project.Server.Modules.User.Service.UserIdService;
import com.ca.project.Server.Modules.User.Mapper.UserIdMapper;
import com.ca.project.core.Exception.BusinessException;
import com.ca.project.core.Respond.ResponseCode;
import com.ca.project.core.Util.IdUtil;
import com.ca.project.core.Util.JwtUtil;
import com.ca.project.core.Util.TimeUtils;
import io.jsonwebtoken.Jwts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author ASUS
 * @description 针对表【user_id】的数据库操作Service实现
 * @createDate 2023-09-04 16:43:45
 */
@Service
public class UserIdServiceImpl extends ServiceImpl<UserIdMapper, UserId> implements UserIdService {

    @Value("${wechat.appid}")
    private String appid;
    @Value("${wechat.secret}")
    private String secret;

    @Autowired
    private UserConverter userConverter;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 用户注册登录
     * <p>
     * 1.实体转化
     * 2.用户注册
     * 3.创建用户基本信息
     * 4.保存redis
     *
     * @param userRegisterContext
     * @return
     */
    @Override
    public String login(UserRegisterContext userRegisterContext) {
        assembleUserEntity(userRegisterContext);
        doRegister(userRegisterContext);
        String token = saveRedis(userRegisterContext);
        return token;
    }

    /**
     * 校验token有效性
     *
     * @param token
     * @return
     */
    @Override
    public boolean valid(String token) {
        Object userId = JwtUtil.analyzeToken(token, RedisConstants.LOGIN_USER_ID);
        if (userId == null)
            throw new BusinessException("token解析失败");
        String key = RedisConstants.LOGIN_USER_ID + userId;
        System.out.println(key);
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(key);
        if (userMap == null || userMap.isEmpty())
            return false;
        boolean isExpiredUpdate = stringRedisTemplate.expire(key, RedisConstants.LOGIN_CODE_TTL, TimeUnit.MILLISECONDS);
        if (!isExpiredUpdate)
            throw new BusinessException("更新token时间失败");
        return true;
    }
/****************************************private***********************************************************************/
    /**
     * 实体转化
     * 由上下文信息转化成用户实体，封装进上下文
     *
     * @param userRegisterContext
     */
    private void assembleUserEntity(UserRegisterContext userRegisterContext) {
        UserId entity = userConverter.userRegisterPO2UserId(userRegisterContext);
        String code = userRegisterContext.getCode();

        if (Objects.isNull(code))
            throw new BusinessException("没有code信息");

        RestTemplate client = new RestTemplate();
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid={appid}&secret={secret}" +
                "&js_code={js_code}&grant_type=authorization_code";
        HashMap<String, Object> map = new HashMap<>();
        map.put("appid", appid);
        map.put("secret", secret);
        map.put("js_code", code);

        ResponseEntity response = client.getForEntity(url, String.class, map);
        String data = (String) response.getBody();
        HashMap<String, String> dataMap = JSON.parseObject(data, HashMap.class);
        String openid = dataMap.get("openid");

        if (Objects.isNull(openid))
            throw new BusinessException("没有code信息");

        entity.setUserId(IdUtil.get());
        entity.setOpenid(openid);
        entity.setTime(TimeUtils.getNowDateTime());
        userRegisterContext.setEntity(entity);
    }

    /**
     * 注册业务实现
     * 1.先去数据中查找有无此openid
     *
     * @param userRegisterContext
     */
    private void doRegister(UserRegisterContext userRegisterContext) {
        UserId entity = userRegisterContext.getEntity();
        String openid = userRegisterContext.getEntity().getOpenid();
        if (Objects.nonNull(entity)) {
            try {
                if (!save(entity))
                    throw new BusinessException("用户注册失败");
            } catch (DuplicateKeyException duException) {
                String time = TimeUtils.getNowDateTime();
                UserId user = new UserId();
                user.setOpenid(openid);
                user.setTime(time);
                if (!updateById(user))
                    throw new BusinessException("更新时间失败");
            }
            return;
        }
        throw new BusinessException(ResponseCode.ERROR);
    }

    /**
     * 保存用户信息到redis中
     *
     * @param userRegisterContext
     */
    private String saveRedis(UserRegisterContext userRegisterContext) {

        Long userId = userRegisterContext.getEntity().getUserId();
        String openid = userRegisterContext.getEntity().getOpenid();

        String token = JwtUtil.generateToken(openid, RedisConstants.LOGIN_USER_ID, userId,
                RedisConstants.LOGIN_CODE_TTL);

        Map<String, String> userMap = new HashMap<>();
        userMap.put("userId", String.valueOf(userId));
        userMap.put("openid", openid);

        System.out.println(RedisConstants.LOGIN_USER_ID + userId);

        stringRedisTemplate.opsForHash().putAll(RedisConstants.LOGIN_USER_ID + userId, userMap);
        stringRedisTemplate.expire(RedisConstants.LOGIN_USER_ID, RedisConstants.LOGIN_CODE_TTL, TimeUnit.MILLISECONDS);

        return token;
    }
}




